//SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

use notice_decode::new_admin_login_notice;
use std::time::Duration;
use tauri::async_runtime::Mutex;
use tauri::menu::{AboutMetadata, Menu, MenuItem, PredefinedMenuItem};
use tauri::tray::{MouseButton, TrayIconBuilder, TrayIconEvent};
use tauri::{App, AppHandle, Emitter, Manager};
use tokio::fs;
use tonic::transport::{Channel, Endpoint};

mod admin_auth_api_cmds;
mod ai_provider_api;
mod board_api;
mod client_cfg_cmds;
mod config_api_cmds;
mod fs_api_cmds;
mod image_utils;
mod notice_decode;
mod user_api_cmds;

#[derive(Default)]
struct GrpcChan(Mutex<Option<Channel>>);

#[derive(Default)]
struct GrpcServerAddr(Mutex<String>);

#[tauri::command]
async fn conn_grpc_server(app_handle: AppHandle, addr: String) -> bool {
    let new_addr = if addr.starts_with("http://") {
        addr
    } else {
        format!("http://{}", &addr)
    };
    let u = url::Url::parse(&new_addr);
    if u.is_err() {
        return false;
    }
    let mut u = u.unwrap();
    if u.port().is_none() {
        if let Err(_) = u.set_port(Some(5000)) {
            return false;
        }
    }
    let new_addr = u.to_string();
    if let Ok(endpoint) = Endpoint::from_shared(String::from(u)) {
        let chan = endpoint
            .connect_timeout(Duration::from_secs(5))
            .tcp_keepalive(Some(Duration::from_secs(30)))
            .concurrency_limit(16)
            .buffer_size(1024 * 1024)
            .connect()
            .await;
        if chan.is_err() {
            return false;
        }
        let chan = chan.unwrap();
        {
            let grpc_chan = app_handle.state::<GrpcChan>().inner();
            *grpc_chan.0.lock().await = Some(chan);
            let gprc_server_addr = app_handle.state::<GrpcServerAddr>().inner();
            *gprc_server_addr.0.lock().await = new_addr;
            return true;
        }
    }
    false
}

#[tauri::command]
async fn is_conn_server(app_handle: AppHandle) -> bool {
    let grpc_chan = app_handle.state::<GrpcChan>().inner();
    let chan = grpc_chan.0.lock().await;
    return chan.is_some();
}

#[tauri::command]
async fn get_conn_server_addr<R: tauri::Runtime>(app_handle: AppHandle<R>) -> String {
    let grpc_server_addr = app_handle.state::<GrpcServerAddr>().inner();
    let addr = grpc_server_addr.0.lock().await;
    return addr.clone();
}

async fn get_grpc_chan<R: tauri::Runtime>(app_handle: &tauri::AppHandle<R>) -> Option<Channel> {
    let grpc_chan = app_handle.state::<GrpcChan>().inner();
    let chan = grpc_chan.0.lock().await;
    return chan.clone();
}

fn get_base_dir() -> Option<String> {
    if let Some(mut home_dir) = dirs::home_dir() {
        home_dir.push(".eteam");
        return Some(home_dir.to_str().unwrap().into());
    }
    None
}

fn get_cache_dir() -> Option<String> {
    if let Some(mut home_dir) = dirs::home_dir() {
        home_dir.push(".eteam");
        home_dir.push("cache");
        return Some(home_dir.to_str().unwrap().into());
    }
    None
}

fn get_tmp_dir() -> Option<String> {
    if let Some(mut home_dir) = dirs::home_dir() {
        home_dir.push(".eteam");
        home_dir.push("tmp");
        return Some(home_dir.to_str().unwrap().into());
    }
    None
}

fn get_user_dir() -> Option<String> {
    if let Some(mut home_dir) = dirs::home_dir() {
        home_dir.push(".eteam");
        home_dir.push("user");
        return Some(home_dir.to_str().unwrap().into());
    }
    None
}

async fn init_local_storage() -> Result<(), Box<dyn std::error::Error>> {
    if let Some(base_dir) = get_base_dir() {
        let meta = fs::metadata((&base_dir).as_str()).await;
        if meta.is_err() {
            if let Err(err) = fs::create_dir_all((&base_dir).as_str()).await {
                return Err(Box::new(err));
            }
        }
    }
    if let Some(cache_dir) = get_cache_dir() {
        let meta = fs::metadata((&cache_dir).as_str()).await;
        if meta.is_err() {
            if let Err(err) = fs::create_dir_all((&cache_dir).as_str()).await {
                return Err(Box::new(err));
            }
        }
    }
    if let Some(tmp_dir) = get_tmp_dir() {
        let meta = fs::metadata((&tmp_dir).as_str()).await;
        if meta.is_err() {
            if let Err(err) = fs::create_dir_all((&tmp_dir).as_str()).await {
                return Err(Box::new(err));
            }
        }
    }
    if let Some(tmp_dir) = get_user_dir() {
        let meta = fs::metadata((&tmp_dir).as_str()).await;
        if meta.is_err() {
            if let Err(err) = fs::create_dir_all((&tmp_dir).as_str()).await {
                return Err(Box::new(err));
            }
        }
    }
    Ok(())
}

fn init_tray(app: &App) {
    let main_window = app.get_webview_window("main").unwrap();
    let icon = app.default_window_icon().unwrap().clone();
    let cfg = app.config();
    let admin_login_i =
        MenuItem::with_id(app, "admin_login", "管理后台", true, None::<&str>).unwrap();
    let sep = PredefinedMenuItem::separator(app).unwrap();
    let about_i = PredefinedMenuItem::about(
        app,
        Some("关于"),
        Some(AboutMetadata {
            name: Some("第E梯队".into()),
            version: cfg.version.clone(),
            short_version: None,
            authors: None,
            comments: None,
            copyright: None,
            license: Some("GPL-3.0-only".into()),
            website: None,
            website_label: None,
            credits: None,
            icon: Some(icon),
        }),
    )
    .unwrap();
    let exit_i = MenuItem::with_id(app, "exit", "退出", true, None::<&str>).unwrap();
    let menu = Menu::with_items(app, &[&admin_login_i, &sep, &about_i, &exit_i]).unwrap();
    let _ = TrayIconBuilder::new()
        .icon(app.default_window_icon().unwrap().clone())
        .menu(&menu)
        .on_tray_icon_event(move |_, ev| {
            if let TrayIconEvent::Click { button, .. } = ev {
                if button == MouseButton::Left {
                    let _ = main_window.show();
                    if let Ok(is_min) = main_window.is_minimizable() {
                        if is_min {
                            let _ = main_window.unminimize();
                        }
                    }
                    let _ = main_window.set_always_on_top(true);
                    let _ = main_window.set_always_on_top(false);
                }
            }
        })
        .on_menu_event(|app, ev| match ev.id().as_ref() {
            "admin_login" => {
                let _ = app.emit_to("main", "notice", new_admin_login_notice());
            }
            "exit" => {
                app.exit(0);
            }
            _ => {}
        })
        .build(app);
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_process::init())
        .plugin(tauri_plugin_updater::Builder::new().build())
        .plugin(tauri_plugin_clipboard_manager::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_opener::init())
        .plugin(tauri_plugin_http::init())
        .plugin(tauri_plugin_fs::init())
        .manage(GrpcChan(Default::default()))
        .manage(GrpcServerAddr(Default::default()))
        .setup(|app| {
            let res = tauri::async_runtime::block_on(async {
                let init_res = init_local_storage().await;
                if init_res.is_err() {
                    return init_res;
                }
                Ok(())
            });
            if res.is_err() {
                return res;
            }
            init_tray(app);
            client_cfg_cmds::init(app);
            admin_auth_api_cmds::init(app);
            user_api_cmds::init(app);
            fs_api_cmds::init(app);
            Ok(())
        })
        .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
            let _ = app
                .get_webview_window("main")
                .expect("no main window")
                .set_focus();
        }))
        .invoke_handler(tauri::generate_handler![
            conn_grpc_server,
            is_conn_server,
            get_conn_server_addr,
            client_cfg_cmds::client_cfg_list_server,
            client_cfg_cmds::client_cfg_save_server_list,
            admin_auth_api_cmds::admin_auth_api_pre_auth,
            admin_auth_api_cmds::admin_auth_api_auth,
            admin_auth_api_cmds::admin_auth_api_get_admin_session,
            admin_auth_api_cmds::admin_auth_api_sign,
            admin_auth_api_cmds::admin_auth_api_logout,
            user_api_cmds::user_api_cmds_login,
            user_api_cmds::user_api_cmds_logout,
            user_api_cmds::user_api_cmds_update_basic,
            user_api_cmds::user_api_cmds_change_passwd,
            user_api_cmds::user_api_cmds_register,
            user_api_cmds::user_api_cmds_get_session,
            user_api_cmds::user_api_cmds_get_user_id,
            fs_api_cmds::fs_api_cmds_stat_local_file,
            fs_api_cmds::fs_api_cmds_get_cache_file,
            fs_api_cmds::fs_api_cmds_download_file,
            fs_api_cmds::fs_api_cmds_write_file_base64,
            fs_api_cmds::fs_api_cmds_write_file,
            fs_api_cmds::fs_api_cmds_write_thumb_image_file,
            fs_api_cmds::fs_api_cmds_set_file_owner,
            fs_api_cmds::fs_api_cmds_get_fs_status,
            fs_api_cmds::fs_api_cmds_save_tmp_file_base64,
            fs_api_cmds::fs_api_cmds_make_tmp_dir,
            fs_api_cmds::fs_api_cmds_get_file_name,
            config_api_cmds::config_api_cmds_get_server_config,

            board_api::board_api_cmds::board_api_cmds_create,
            board_api::board_api_cmds::board_api_cmds_update_basic,
            board_api::board_api_cmds::board_api_cmds_update_node_panel_setting,
            board_api::board_api_cmds::board_api_cmds_remove,
            board_api::board_api_cmds::board_api_cmds_list_my,
            board_api::board_api_cmds::board_api_cmds_get,
            board_api::board_api_cmds::board_api_cmds_add_llm,
            board_api::board_api_cmds::board_api_cmds_update_llm,
            board_api::board_api_cmds::board_api_cmds_remove_llm,

            board_api::board_member_api_cmds::board_member_api_cmds_set_admin,
            board_api::board_member_api_cmds::board_member_api_cmds_update_view_time,
            board_api::board_member_api_cmds::board_member_api_cmds_remove,
            board_api::board_member_api_cmds::board_member_api_cmds_leave,
            board_api::board_member_api_cmds::board_member_api_cmds_list_my,
            board_api::board_member_api_cmds::board_member_api_cmds_list,
            board_api::board_member_api_cmds::board_member_api_cmds_get,
            board_api::board_member_api_cmds::board_member_api_cmds_gen_invite,
            board_api::board_member_api_cmds::board_member_api_cmds_join,

            board_api::board_draw_api_cmds::board_draw_api_cmds_create_draw,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_draw_basic,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_draw_edit_member,
            board_api::board_draw_api_cmds::board_draw_api_cmds_remove_draw,
            board_api::board_draw_api_cmds::board_draw_api_cmds_list_draw,
            board_api::board_draw_api_cmds::board_draw_api_cmds_get_draw,
            board_api::board_draw_api_cmds::board_draw_api_cmds_create_node,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_node_content,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_node_position,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_node_size,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_node_style,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_node_lock,
            board_api::board_draw_api_cmds::board_draw_api_cmds_list_node,
            board_api::board_draw_api_cmds::board_draw_api_cmds_get_node,
            board_api::board_draw_api_cmds::board_draw_api_cmds_remove_node,
            board_api::board_draw_api_cmds::board_draw_api_cmds_create_edge,
            board_api::board_draw_api_cmds::board_draw_api_cmds_update_edge_basic,
            board_api::board_draw_api_cmds::board_draw_api_cmds_list_edge,
            board_api::board_draw_api_cmds::board_draw_api_cmds_get_edge,
            board_api::board_draw_api_cmds::board_draw_api_cmds_remove_edge,
            board_api::board_draw_api_cmds::board_draw_api_cmds_remove_edge_by_handle,
            board_api::board_draw_api_cmds::board_draw_api_cmds_report_mouse_position,

            board_api::board_extdata_api_cmds::board_extdata_api_cmds_add,
            board_api::board_extdata_api_cmds::board_extdata_api_cmds_update,
            board_api::board_extdata_api_cmds::board_extdata_api_cmds_remove,
            board_api::board_extdata_api_cmds::board_extdata_api_cmds_list,
            board_api::board_extdata_api_cmds::board_extdata_api_cmds_get,

            board_api::board_chat_api_cmds::board_chat_api_cmds_send,
            board_api::board_chat_api_cmds::board_chat_api_cmds_list_after,
            board_api::board_chat_api_cmds::board_chat_api_cmds_list_before,
            board_api::board_chat_api_cmds::board_chat_api_cmds_get_unread_count,
            board_api::board_chat_api_cmds::board_chat_api_cmds_clear_unread,


            ai_provider_api::ai_provider_api_cmds::ai_provider_api_cmds_list_llm_provider,
            ai_provider_api::ai_provider_admin_api_cmds::ai_provider_admin_api_cmds_add_llm_provider,
            ai_provider_api::ai_provider_admin_api_cmds::ai_provider_admin_api_cmds_update_llm_provider,
            ai_provider_api::ai_provider_admin_api_cmds::ai_provider_admin_api_cmds_remove_llm_provider,

            ai_provider_api::llm_cmds::llm_cmds_chat_completion,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
